#include "textrenderer.h"
#include "factories.h"
#include "vecmath.h"

/*
#define FNT_BITMAP_8_BY_13         0
#define FNT_BITMAP_9_BY_15         1
#define FNT_BITMAP_HELVETICA_10    2
#define FNT_BITMAP_HELVETICA_12    3
#define FNT_BITMAP_HELVETICA_18    4
#define FNT_BITMAP_TIMES_ROMAN_10  5
#define FNT_BITMAP_TIMES_ROMAN_24  6

fntBitmapFont *fntGetBitmapFont(int id);
*/


TextRenderer::TextRenderer()
: textRenderer(NULL), maxPixelHeight(16.0f), outputVerticalAlignment(Left), outputHorizontalAlignment(Center), inputVerticalAlignment(Left), inputHorizontalAlignment(Center)
{
}

TextRenderer::~TextRenderer()
{
    if (textRenderer)
        delete textRenderer;
}

void TextRenderer::setup()
{
    fntInit();
    
    textRenderer = new fntRenderer();
//    textRenderer->setFont(new fntTexFont("/Users/lenaic/Development/Scriptie/plib_examples-1.8.5/src/fnt/data/AvantGarde-Book.txf", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR));
    textRenderer->setFont(new fntTexFont("AvantGarde-Book.txf", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR));
    textRenderer->setPointSize( 1.0f );

    textRenderer->getFont()->getBBox("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 #", 1, 0, &bbLeft, &bbRight, &bbBottom, &bbTop);
    bbWidth = bbRight - bbLeft;
    bbHeight = (bbTop - bbBottom);
    meanNormCharWidth = bbWidth / (bbHeight * 64.0f);
	
#if GL_EXT_texture_filter_anisotropic
	begin();
	GLfloat largestSupportedAnisotropy;
	glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largestSupportedAnisotropy);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, largestSupportedAnisotropy);
	end();
#endif
}

void TextRenderer::begin()
{
    glColor4ubv(ColorFactory::instance().getTextColor());
    
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glAlphaFunc(GL_GREATER, 0.1);
    
    glEnable(GL_BLEND);
    glEnable(GL_ALPHA_TEST);
    textRenderer->begin();
}

void TextRenderer::end()
{
    textRenderer->end();
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_ALPHA_TEST);
    glDisable(GL_BLEND);
}

float TextRenderer::fitString(char* out, const char* str, float height, float width)
{
    float normWidth = width / height;
    unsigned length = (unsigned)ceilf(normWidth / meanNormCharWidth);
	unsigned strLength = strlen(str);
	if (length > strLength)
		length = strLength;
    
    float stdWidth = normWidth * bbHeight;
    strcpy(out, str);
    out[length] = 0;
    float left, right, dummy;
    textRenderer->getFont()->getBBox(out, 1, 0, &left, &right, &dummy, &dummy);
    while (right - left > stdWidth && length >= 1) {
        length--;
        out[length] = 0;
        textRenderer->getFont()->getBBox(out, 1, 0, &left, &right, &dummy, &dummy);
    }
	
	// Return the width in the same space as the parameters of the function
	return (right - left) * height / bbHeight;
}

void TextRenderer::drawWithCamera(const char* str, Camera& camera, float height, float width)
{
    // Shoot a ray from the center of the screen to get the world position at the centre of the screen
    Vec3f normCenterStart = {0, 0, -1};
    Vec3f normCenterEnd = {0, 0, 1};
    Vec3f worldCenter;
    camera.getNormalizedSpacePickRay(normCenterStart, normCenterEnd).getPoint(worldCenter, 0);
    
    // Calculate the height in pixels available for the text
    Vec3f worldOffset;
	Vec3f normalOffset;
	camera.normal(normalOffset);
	ScaleVec3f(normalOffset, normalOffset, height);
    SetVec3f(worldOffset, worldCenter);
	AddVec3f(worldOffset, worldOffset, normalOffset);
    
    Mat4f worldToScreen;
    camera.worldToScreenMat4f(worldToScreen);
    Vec3f screenStart;
    Vec3f screenEnd;
    MultiplyMat4fVec3f(screenStart, worldToScreen, worldCenter);
    MultiplyMat4fVec3f(screenEnd, worldToScreen, worldOffset);
    Vec3f screenVect;
    SubtractVec3f(screenVect, screenEnd, screenStart);
    float pixelHeight = sqrtf(InnerProductVec3f(screenVect, screenVect));
    
    // If the height is big enough, trade some height in for more width (ratio preserving scaling of font)
    float pixelScale = maxPixelHeight / pixelHeight;
    if (pixelScale > 1.0f)
        pixelScale = 1.0f;
    
	// Fit the string to the available width
	float fittedWidth = fitString(buffer, str, height, width / pixelScale);
    
	// Apply the scaling to normalized sizes together with the possible pixel scaling
    float scale = (height / bbHeight) * pixelScale;
	float scaledTextHeight = height * pixelScale;
	float scaledTextWidth = fittedWidth * pixelScale;
    
	// Asjust the translation to match the alignment settings
	Vec3f translation = {0, 0, 0};
	if (inputVerticalAlignment == Top) {
		if (outputVerticalAlignment == Top)
			translation[1] -= scaledTextHeight;
		else if (outputVerticalAlignment == Center)
			translation[1] -= (height + scaledTextHeight) / 2.0f;
		else if (outputVerticalAlignment == Bottom)
			translation[1] -= height;
	}
	else if (inputVerticalAlignment == Center) {
		if (outputVerticalAlignment == Top)
			translation[1] += height / 2.0f - scaledTextHeight;
		else if (outputVerticalAlignment == Center)
			translation[1] -= scaledTextHeight / 2.0f;
		else if (outputVerticalAlignment == Bottom)
			translation[1] -= height / 2.0f;
	}
	else if (inputVerticalAlignment == Bottom) {
		if (outputVerticalAlignment == Top)
			translation[1] += height - scaledTextHeight;
		else if (outputVerticalAlignment == Center)
			translation[1] += (height - scaledTextHeight) / 2.0f;
	}
	
	if (inputHorizontalAlignment == Right) {
		if (outputHorizontalAlignment == Right)
			translation[0] -= scaledTextWidth;
		else if (outputHorizontalAlignment == Center)
			translation[0] -= (width + scaledTextWidth) / 2.0f;
		else if (outputHorizontalAlignment == Left)
			translation[0] -= width;
	}
	else if (inputHorizontalAlignment == Center) {
		if (outputHorizontalAlignment == Right)
			translation[0] += width / 2.0f - scaledTextWidth;
		else if (outputHorizontalAlignment == Center)
			translation[0] -= scaledTextWidth / 2.0f;
		else if (outputHorizontalAlignment == Left)
			translation[0] -= width / 2.0f;
	}
	else if (inputHorizontalAlignment == Left) {
		if (outputHorizontalAlignment == Right)
			translation[0] += width - scaledTextWidth;
		else if (outputHorizontalAlignment == Center)
			translation[0] += (width - scaledTextWidth) / 2.0f;
	}
	
    glPushMatrix();
	glTranslatef(translation[0], translation[1], translation[2]);
    glScalef(scale, scale, 1.0);
	glTranslatef(0, -bbBottom, 0);
    
    textRenderer->start3f(0, 0, 0);
    textRenderer->puts(buffer);
    glPopMatrix();
}

BoundingBox TextRenderer::getWorldBoundingBoxWithCamera(const char* str, Camera& camera, float height, float width)
{
    // Shoot a ray from the center of the screen to get the world position at the centre of the screen
    Vec3f normCenterStart = {0, 0, -1};
    Vec3f normCenterEnd = {0, 0, 1};
    Vec3f worldCenter;
    camera.getNormalizedSpacePickRay(normCenterStart, normCenterEnd).getPoint(worldCenter, 0);
    
    // Calculate the height in pixels available for the text
    Vec3f worldOffset;
	Vec3f normalOffset;
	camera.normal(normalOffset);
	ScaleVec3f(normalOffset, normalOffset, height);
    SetVec3f(worldOffset, worldCenter);
	AddVec3f(worldOffset, worldOffset, normalOffset);
    
    Mat4f worldToScreen;
    camera.worldToScreenMat4f(worldToScreen);
    Vec3f screenStart;
    Vec3f screenEnd;
    MultiplyMat4fVec3f(screenStart, worldToScreen, worldCenter);
    MultiplyMat4fVec3f(screenEnd, worldToScreen, worldOffset);
    Vec3f screenVect;
    SubtractVec3f(screenVect, screenEnd, screenStart);
    float pixelHeight = sqrtf(InnerProductVec3f(screenVect, screenVect));
    
    // If the height is big enough, trade some height in for more width (ratio preserving scaling of font)
    float pixelScale = maxPixelHeight / pixelHeight;
    if (pixelScale > 1.0f)
        pixelScale = 1.0f;
    
	// Fit the string to the available width
	float fittedWidth = fitString(buffer, str, height, width / pixelScale);
    
	// Apply the scaling to normalized sizes together with the possible pixel scaling
    float scale = (height / bbHeight) * pixelScale;
	float scaledTextHeight = height * pixelScale;
	float scaledTextWidth = fittedWidth * pixelScale;
    
	// Asjust the translation to match the alignment settings
	Vec3f translation = {0, 0, 0};
	if (inputVerticalAlignment == Top) {
		if (outputVerticalAlignment == Top)
			translation[1] -= scaledTextHeight;
		else if (outputVerticalAlignment == Center)
			translation[1] -= (height + scaledTextHeight) / 2.0f;
		else if (outputVerticalAlignment == Bottom)
			translation[1] -= height;
	}
	else if (inputVerticalAlignment == Center) {
		if (outputVerticalAlignment == Top)
			translation[1] += height / 2.0f - scaledTextHeight;
		else if (outputVerticalAlignment == Center)
			translation[1] -= scaledTextHeight / 2.0f;
		else if (outputVerticalAlignment == Bottom)
			translation[1] -= height / 2.0f;
	}
	else if (inputVerticalAlignment == Bottom) {
		if (outputVerticalAlignment == Top)
			translation[1] += height - scaledTextHeight;
		else if (outputVerticalAlignment == Center)
			translation[1] += (height - scaledTextHeight) / 2.0f;
	}
	
	if (inputHorizontalAlignment == Right) {
		if (outputHorizontalAlignment == Right)
			translation[0] -= scaledTextWidth;
		else if (outputHorizontalAlignment == Center)
			translation[0] -= (width + scaledTextWidth) / 2.0f;
		else if (outputHorizontalAlignment == Left)
			translation[0] -= width;
	}
	else if (inputHorizontalAlignment == Center) {
		if (outputHorizontalAlignment == Right)
			translation[0] += width / 2.0f - scaledTextWidth;
		else if (outputHorizontalAlignment == Center)
			translation[0] -= scaledTextWidth / 2.0f;
		else if (outputHorizontalAlignment == Left)
			translation[0] -= width / 2.0f;
	}
	else if (inputHorizontalAlignment == Left) {
		if (outputHorizontalAlignment == Right)
			translation[0] += width - scaledTextWidth;
		else if (outputHorizontalAlignment == Center)
			translation[0] += (width - scaledTextWidth) / 2.0f;
	}
	
	BoundingBox bb;
	SetVec3f(bb.min, translation[0], translation[1] + scale * bbBottom, translation[2]);
	SetVec3f(bb.max, bb.min[0] + scaledTextWidth, bb.min[1] + scaledTextHeight, bb.min[2]);
	
	return bb;
}
